#define BAUD 9600

#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>

#include "leds.h"
#include "filters.h"
#include "beatfilter.h"

unsigned char mode = 0;

rgb base;
short basevalue;

void getcolor(rgb* omg, unsigned char h)
{
	int hi = (h / 43) % 6;
	
	int f = hi - hi*43;
	
	switch(hi)
	{
		#define cv(i, cr,cg,cb) case i:omg->r=cr;omg->g=cg;omg->b=cb;break;
		
		cv(0, 1,  f,  0  )
		cv(1, 1-f,1,  0  )
		cv(2, 0,  1,  f  )
		cv(3, 0,  1-f,1  )
		cv(4, f,  0,  1  )
		cv(5, 1,  0,  1-f)
		default:
			//crap
			break;
	}
}

ISR(TIMER0_OVF_vect)
{
	basevalue++;
	if((basevalue & 0xFF) == 0)
		getcolor(&base, basevalue >> 8);
	
	switch(mode)
	{
		case 0:
			filtersample(ADCH);
			break;
		case 1 ... 7:
			beatfilter(ADCH);
			break;
		default:
			break;
	}
}

void init(void)
{
	// IO
	DDRA  = 0x00;
	PORTA = 0xFF;
	
	//Timer
	TCCR0 = 1;
	TIMSK = _BV(TOIE0);
	
	//USART
	UCSR0A = 0;
	UCSR0B = _BV(RXEN) | _BV(RXCIE) | _BV(TXEN) | _BV(TXCIE);
	UCSR0C = 6;
	
	UBRR0H = UBRRH_VALUE;
	UBRR0L = UBRRL_VALUE;
	#if USE_2X
	UCSR0A |= (1 << U2X);
	#else
	UCSR0A &= ~(1 << U2X);
	#endif
	
	//ADC
	ADMUX = _BV(ADLAR);
	ADCSRA = _BV(ADEN) | _BV(ADFR) | _BV(ADSC);
}

void sound(void)
{
	for(int i=0; i<ledcount(); i++)
		setcolor(i, mulcolor(base, filterval(i)/128));
}

void effect0(void)
{
	int max = beatvalue();
	
	int num = max / 256;
	int locnum = max % 256;
	
	for(int i = 0; i<num; i++)
		setcolor(i, base);
	
	setcolor(num, mulcolor(base, locnum));
}

void effect1(void)
{
	int max = beatvalue();
	
	int num = max / 1024;
	int locnum = max % 1024;
	
	for(int i = 0; i<num; i++)
		setcolor(i, base);
	
	setcolor(num, mulcolor(base, locnum));
}

void effect2(void)
{
	int max = beatvalue();
	
	int num = max / 4096;
	int locnum = max % 4096;
	
	for(int i = 0; i<num; i++)
		setcolor(i, base);
	
	setcolor(num, mulcolor(base, locnum));
}

void effectx(unsigned short omg)
{
	int max = beatvalue();
	
	int num = max / omg;
	int locnum = max % omg;
	
	for(int i = 0; i<num; i++)
		setcolor(i, base);
	
	setcolor(num, mulcolor(base, locnum));
}

void coloreffect(void)
{
	rgb omg;
	unsigned char omgcolor = basevalue >> 8;
	unsigned char addend = ledcount() / 256;
	
	for(int i = 0; i<ledcount(); i++)
	{
		getcolor(&omg, omgcolor+addend*i);
		setcolor(i, omg);
	}
}

void control(void)
{
	mode = PINA;
	
	switch(mode)
	{
		case 0:
			sound();
			break;
		case 1:
			effect0();
			break;
		case 2:
			effect1();
			break;
		case 3:
			effect2();
			break;
		case 4:
			effectx(128);
			break;
		case 5:
			effectx(64);
			break;
		case 6:
			effectx(32);
			break;
		case 7:
			effectx(8192);
			break;
		case 128:
			coloreffect();
			break;
		default:
			break;
	}
}

int main(void)
{
	init();
	
	sei();
	
	leddetect();
	
	for(int i=0; i<ledcount(); i++)
		setcolor(i, black);
	
	while(1)
		control();
	
	return 0;
}
